
#include "Ymodem.h"

#if		YMODEM_SEND_ENABLE == 1

/*************************************************************
  Function   : YModem_PrepareFirstPacket 
  Description: 准备第一个数据包，包括文件名与大小
  Input      : data-要发送的数据包
               fileName-文件名
	              length-文件的大小        
  return     : none    
*************************************************************/
void YModem_PrepareFirstPacket(unsigned char *pak,unsigned  char *fileName, unsigned int length)
{
	unsigned int i;

	pak[0] = SOH;	               //128字节数据包
	pak[1] = 0x00;	               //第一个数据包
	pak[2] = 0xFF;		       //data[2] = ~data[1]

	memset(pak + PACKET_HEADER, 0 , PACKET_SIZE); //0填充

	for(i = 0; (fileName[i] != '\0') && (i < FILE_NAME_LENGTH); i++)
	{
		pak[i + PACKET_HEADER] = fileName[i];//拷贝文件名
	}
	pak[i + PACKET_HEADER] = '\0';//文件名以'\0'结束

	sprintf((char *)(pak + i + PACKET_HEADER + 1),"%u",length);//将文件长度转化成字符串
}

/*************************************************************
  Function   : YModem_PrepareDataPacket 
  Description: 准备数据包
  Input      : sourceBuf-要发送的数据
               data-要发送的数据包   
	       ptkNo-数据包的序号
	       sizeBlk-要发送的数据长度     
  return     : none    
*************************************************************/
void YModem_PrepareDataPacket(unsigned char *source,unsigned char *pak, unsigned char pakNo, unsigned int sendLen)
{
	 unsigned int i, size, packetSize;
	 unsigned char *file_ptr;

	 packetSize = (sendLen >= PACKET_1K_SIZE) ? PACKET_1K_SIZE : PACKET_SIZE;//决定发送128字节数据包还是发送1024字节数据包
	 size = (sendLen < packetSize) ? sendLen : packetSize; //计算数据包中数据的长度
	 if(packetSize == PACKET_1K_SIZE)//1K字节数据
	 {
	 	pak[0] = STX;  	//设置数据包首部STX，1024字节数据包
	 }else				//128字节数据
	 {
	 	pak[0] = SOH; 	//设置数据包首部SOH,128字节数据
	 }
	 pak[1] = pakNo;   	//数据包序号
	 pak[2] = (~pakNo);
	 file_ptr = source; //指向需要发送的数据

	 for(i = PACKET_HEADER; i < size + PACKET_HEADER; i++)
	 {
	 	pak[i] = *file_ptr++;  //拷贝要发送的数据
	 }
	 if(size <= packetSize)		//数据长度小于128字节
	 {
	 	for(i = size + PACKET_HEADER; i < packetSize + PACKET_HEADER; i++)
		{
			pak[i]	= 0x1A;	//数据不够，以0x1A填充
		}
	 }
}

/*************************************************************
  Function   : YModem_PrepareLastPacket 
  Description: 准备最后一个数据包
  Input      : data-要发送数据包
  return     : none    
*************************************************************/
void YModem_PrepareLastPacket(unsigned char *pak)
{
	unsigned int i;

	pak[0] = SOH;	 //128字节数据包
	pak[1] = 0;     //序号
	pak[2] = 0xFF;
	for(i = PACKET_HEADER; i < (PACKET_SIZE + PACKET_HEADER); i++)
	{
		pak[i] = 0x00;//数据以0填充，即空数据包
	}	
}



/*************************************************************
  Function   : YModem_SendPacket 
  Description: 发送数据包
  Input      : data-要发送的数据
               length-发送的数据长度        
  return     : none    
*************************************************************/
void YModem_SendPacket(unsigned char *pak, unsigned int length)
{
	unsigned int i = 0;
	while(i < length)
	{
		YModem_SendByte(pak[i++]);
	}
}

/*************************************************************
  Function   : YModem_TransmitFirstPacket 
  Description: 传输第一个数据包
  Input      : sendFileName-文件名
               fileSize-文件的大小        
  return     : 0-successed -1 -failed    
*************************************************************/
int YModem_TransmitFirstPacket(unsigned char *sendFileName, unsigned int fileSize)
{
	unsigned char    errors, receiveChar1,receiveChar2;
	unsigned int     tempCRC;	

	errors = 0;
	receiveChar1 = 0;
	receiveChar2 = 0;
			
	YModem_PrepareFirstPacket(packet_data, sendFileName, fileSize);//准备第一个数据包
	do
	{
		YModem_SendPacket(packet_data, PACKET_SIZE + PACKET_HEADER);//发送第一个数据包
		tempCRC = W_CRC16((uint8_t *)&packet_data[3], PACKET_SIZE);	//计算校验码
		YModem_SendByte(tempCRC >> 8);	                  	//发送CRC高位
		YModem_SendByte(tempCRC & 0xFF);                  	//发送CRC低位

		if(((YModem_RecvByte(&receiveChar1, NAK_TIMEOUT) == 0) && (receiveChar1 == ACK))
		 && ((YModem_RecvByte(&receiveChar2, NAK_TIMEOUT) == 0) && (receiveChar2 == CRC16)))
		{
			break;//先后收到ACK与C标志，才表示接收方接收成功
		}else
		{
			errors++;
			if(errors >= 0x0A)
			{
				return -1;
			}			
		}
	}while(1);   
	return 0;
}

/*************************************************************
  Function   : YModem_TransmitDataPacket 
  Description: 开始传输数据包
  Input      : buf-要传输的数据
               fileSize-要发送文件的大小        
  return     : 0-successed -1 -failed    
*************************************************************/
int YModem_TransmitDataPacket(unsigned char *buf, unsigned int size)
{
	unsigned char    errors, receiveChar1;
	unsigned int     tempCRC;
		
	unsigned char *buf_ptr;
	unsigned char  blkNumber; 
	unsigned int   pktSize ;
	unsigned int   fileSize;

	blkNumber = 0x01;
	buf_ptr = buf;
	fileSize = size; 
	while(fileSize)	      //数据没有发送完
	{
		errors = 0;
		YModem_PrepareDataPacket(buf_ptr, packet_data, blkNumber, fileSize);//准备数据包
		do
		{
			if(fileSize >= PACKET_1K_SIZE) //1024字节数据包
			{
				pktSize = PACKET_1K_SIZE ;
			}else		//128字节数据包
			{
				pktSize = PACKET_SIZE ;
			}
			YModem_SendPacket(packet_data, pktSize+ PACKET_HEADER);		//发送数据包

			tempCRC = W_CRC16((uint8_t *)&packet_data[3], pktSize);	//计算校验码
			YModem_SendByte(tempCRC >> 8);	              	//发送CRC高位
			YModem_SendByte(tempCRC & 0xFF);              	//发送CRC低位
			
			receiveChar1 = 0;
			YModem_RecvByte(&receiveChar1, NAK_TIMEOUT);//
			if(receiveChar1 == ACK)// 
			{
				//收到ACK
				if(fileSize > pktSize)
				{
					buf_ptr += pktSize;//偏移要发送数据的位置
					fileSize -= pktSize;//计算剩余的数据
					if(blkNumber == (size/1024 + 3))//数据包是否还需发送
					{
						return -1;
					}else 
					{
						blkNumber++;					  
					}
				}else
				{
					buf_ptr += pktSize;
					fileSize = 0;
				}
				break;
			}else
			{
				errors++;
				if(errors >= 0x0A)
				{
					return -1;
				}				
			}
		}while (1);
	}
	return 0;
}

/*************************************************************
  Function   : YModem_TransmitLastPacket 
  Description: 传输最后一个数据包
  Input      :  none       
  return     : 0-successed -1 -failed    
*************************************************************/
int YModem_TransmitLastPacket(void)
{
	unsigned char    ackReceived, errors, receiveChar;
	unsigned int     tempCRC;

	ackReceived = 0;
	receiveChar = 0;
	errors = 0;
	YModem_PrepareLastPacket(packet_data);
	do
	{
		YModem_SendPacket(packet_data, PACKET_SIZE + PACKET_HEADER);//发送数据包
		tempCRC = W_CRC16((uint8_t *)&packet_data[3], PACKET_SIZE);//计算CRC检验位
        YModem_SendByte(tempCRC >> 8);	//发送CRC高位
        YModem_SendByte(tempCRC & 0xFF);//发送CRC低位

		if((YModem_RecvByte(&receiveChar, NAK_TIMEOUT) == 0) && 
		   (receiveChar == ACK))
		{
			ackReceived = 1; //收到ACK
		}else
		{
			errors++;
		}
	}while(!ackReceived && (errors < 0x0A));
	if(errors >= 0x0A)
	{
		return -1;
	}
	return 0;
}

/*************************************************************
  Function   : YModem_TransmitFirstEOT 
  Description: 发送第一个EOT结束标志
  Input      : none        
  return     : 0-successed -1 -failed    
*************************************************************/
int YModem_TransmitFirstEOT(void)
{
	unsigned char    ackReceived, errors, receiveChar;

	ackReceived = 0;
	receiveChar = 0;
	errors = 0;
	do
	{
		YModem_SendByte(EOT);//发送结束标志
		if((YModem_RecvByte(&receiveChar, NAK_TIMEOUT) == 0) && (receiveChar == NAK))
		{
			ackReceived = 1;//收到ACK, 表示对方收到EOT标志
		}else
		{
			errors++;
		}
	}while(!ackReceived && (errors < 0x0A));
	if(errors >= 0x0A)
	{
		return -1;
	}
	return 0;
}

/*************************************************************
  Function   : YModem_TransmitSecondEOT 
  Description: 发送第二个EOT结束标志
  Input      :         
  return     : 0-successed -1 -failed    
*************************************************************/
int YModem_TransmitSecondEOT(void)
{
	unsigned char    ackReceived, errors, receiveChar;

	ackReceived = 0;
	receiveChar = 0;
	errors = 0;
	do
	{
		YModem_SendByte(EOT);	  //发送结束标志
		if(((YModem_RecvByte(&receiveChar, NAK_TIMEOUT) == 0) && (receiveChar == ACK))
		&& ((YModem_RecvByte(&receiveChar, NAK_TIMEOUT) == 0) && (receiveChar == CRC16)))
		{
			ackReceived = 1;//分别收到ACK与C标志，才表示对方同意结束
		}else
		{
			errors++;
		}
	}while(!ackReceived && (errors < 0x0A));
	if(errors >= 0x0A)
	{
		return -1;
	}
	return 0;
}

/*************************************************************
  Function   : YModem_Transmit 
  Description: ymodem传输文件
  Input      : buf-要传输的数据
               sendFileName-传传输的文件名
			   filesize-传输的数据大小        
  return     : 0-successed -1 -failed    
*************************************************************/
int YModem_Transmit(unsigned char *buf,unsigned char *sendFileName, unsigned int fileSize)
{
	int result = 0;
	
	result = YModem_TransmitFirstPacket(sendFileName, fileSize);//发送一个数据包
	if(result != 0) return result;
	result = YModem_TransmitDataPacket(buf, fileSize);//发送文件数据
	if(result != 0) return result;
	result = YModem_TransmitFirstEOT();//发送第一个结束标志
	if(result != 0) return result;	
	result = YModem_TransmitSecondEOT();//发送第二个结束标志
	if(result != 0) return result;	
	result = YModem_TransmitLastPacket();//发送最后一个数据包
	if(result != 0) return result;
	return 0;;
}

#endif


